#include <linux/irq.h>

struct irq_data *irq_get_irq_data(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	return desc ? &desc->irq_data : NULL;
}

static void __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
		     int is_chained, const char *name)
{
	// printf("this is %s(): %d\r\n", __func__, __LINE__);

// 	if (!handle) {
// 		handle = handle_bad_irq;
// 	} else {
// 		struct irq_data *irq_data = &desc->irq_data;
// #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
// 		/*
// 		 * With hierarchical domains we might run into a
// 		 * situation where the outermost chip is not yet set
// 		 * up, but the inner chips are there.  Instead of
// 		 * bailing we install the handler, but obviously we
// 		 * cannot enable/startup the interrupt at this point.
// 		 */
// 		while (irq_data) {
// 			if (irq_data->chip != &no_irq_chip)
// 				break;
// 			/*
// 			 * Bail out if the outer chip is not set up
// 			 * and the interrupt supposed to be started
// 			 * right away.
// 			 */
// 			if (WARN_ON(is_chained))
// 				return;
// 			/* Try the parent */
// 			irq_data = irq_data->parent_data;
// 		}
// #endif
// 		if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip))
// 			return;
// 	}

	/* Uninstall? */
	// if (handle == handle_bad_irq) {
	// 	if (desc->irq_data.chip != &no_irq_chip)
	// 		mask_ack_irq(desc);
	// 	irq_state_set_disabled(desc);
	// 	if (is_chained)
	// 		desc->action = NULL;
	// 	desc->depth = 1;
	// }
	desc->handle_irq = handle;
	desc->name = name;

	// if (handle != handle_bad_irq && is_chained) {
	// 	unsigned int type = irqd_get_trigger_type(&desc->irq_data);

	// 	/*
	// 	 * We're about to start this interrupt immediately,
	// 	 * hence the need to set the trigger configuration.
	// 	 * But the .set_type callback may have overridden the
	// 	 * flow handler, ignoring that we're dealing with a
	// 	 * chained interrupt. Reset it immediately because we
	// 	 * do know better.
	// 	 */
	// 	if (type != IRQ_TYPE_NONE) {
	// 		__irq_set_trigger(desc, type);
	// 		desc->handle_irq = handle;
	// 	}

	// 	irq_settings_set_noprobe(desc);
	// 	irq_settings_set_norequest(desc);
	// 	irq_settings_set_nothread(desc);
	// 	desc->action = &chained_action;
	// 	irq_activate_and_startup(desc, IRQ_RESEND);
	// }
}

void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
		  const char *name)
{
	unsigned long flags;
	// struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
	struct irq_desc *desc = &irq_desc[irq];

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (!desc)
		return;

	__irq_do_set_handler(desc, handle, is_chained, name);
	// irq_put_desc_busunlock(desc, flags);
}

static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip)
{
	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	// if (!(desc->istate & IRQS_ONESHOT)) {
		chip->irq_eoi(&desc->irq_data);
	// 	return;
	// }
	/*
	 * We need to unmask in the following cases:
	 * - Oneshot irq which did not wake the thread (caused by a
	 *   spurious interrupt or a primary handler handling it
	 *   completely).
	 */
	// if (!irqd_irq_disabled(&desc->irq_data) &&
	//     irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) {
	// 	chip->irq_eoi(&desc->irq_data);
	// 	unmask_irq(desc);
	// } else if (!(chip->flags & IRQCHIP_EOI_THREADED)) {
	// 	chip->irq_eoi(&desc->irq_data);
	// }
}

void handle_fasteoi_irq(struct irq_desc *desc)
{
	struct irq_chip *chip = desc->irq_data.chip;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	// raw_spin_lock(&desc->lock);

	// if (!irq_may_run(desc))
	// 	goto out;

	// desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

	/*
	 * If its disabled or no action available
	 * then mask it and get out of here:
	 */
	// if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
	// 	desc->istate |= IRQS_PENDING;
	// 	mask_irq(desc);
	// 	goto out;
	// }

	// kstat_incr_irqs_this_cpu(desc);
	// if (desc->istate & IRQS_ONESHOT)
	// 	mask_irq(desc);

	handle_irq_event(desc);

	cond_unmask_eoi_irq(desc, chip);

	// raw_spin_unlock(&desc->lock);
	return;
// out:
	// if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
	// 	chip->irq_eoi(&desc->irq_data);
	// raw_spin_unlock(&desc->lock);
}

void unmask_irq(struct irq_desc *desc)
{
	// if (!irqd_irq_masked(&desc->irq_data))
	// 	return;

	if (desc->irq_data.chip->irq_unmask) {
		desc->irq_data.chip->irq_unmask(&desc->irq_data);
		// irq_state_clr_masked(desc);
	}
}

void irq_enable(struct irq_desc *desc)
{
	// if (!irqd_irq_disabled(&desc->irq_data)) {
		unmask_irq(desc);
	// } else {
	// 	irq_state_clr_disabled(desc);
	// 	if (desc->irq_data.chip->irq_enable) {
	// 		desc->irq_data.chip->irq_enable(&desc->irq_data);
	// 		irq_state_clr_masked(desc);
	// 	} else {
	// 		unmask_irq(desc);
	// 	}
	// }
}

int irq_startup(struct irq_desc *desc, bool resend, bool force)
{
	// struct irq_data *d = irq_desc_get_irq_data(desc);
	// struct cpumask *aff = irq_data_get_affinity_mask(d);
	int ret = 0;

	// desc->depth = 0;

	// if (irqd_is_started(d)) {
		irq_enable(desc);
	// } else {
	// 	switch (__irq_startup_managed(desc, aff, force)) {
	// 	case IRQ_STARTUP_NORMAL:
	// 		if (d->chip->flags & IRQCHIP_AFFINITY_PRE_STARTUP)
	// 			irq_setup_affinity(desc);
	// 		ret = __irq_startup(desc);
	// 		if (!(d->chip->flags & IRQCHIP_AFFINITY_PRE_STARTUP))
	// 			irq_setup_affinity(desc);
	// 		break;
	// 	case IRQ_STARTUP_MANAGED:
	// 		irq_do_set_affinity(d, aff, false);
	// 		ret = __irq_startup(desc);
	// 		break;
	// 	case IRQ_STARTUP_ABORT:
	// 		irqd_set_managed_shutdown(d);
	// 		return 0;
	// 	}
	// }
	// if (resend)
	// 	check_irq_resend(desc, false);

	return ret;
}
